package edu.byu.intex2.data.dao;

import edu.byu.intex2.data.bo.NewCar; //What's this?
import edu.byu.intex2.data.Cache;
import edu.byu.intex2.data.ConnectionPool;
import edu.byu.intex2.data.DataException;
import java.util.List;
import java.sql.*;
import java.util.LinkedList;

/**
 * Physical Car Data Access Object
 * @author Jason Whiting
 */
public class NewCarDAO
{

  /////////////////////////////////////////////
  ///   Singleton code

  private static NewCarDAO instance = null;

  /** Creates a new instance of NewCarDAO */
  private NewCarDAO()
  {

  }//constructor

  /** Retrieves the single instance of this class */
  public static synchronized NewCarDAO getInstance()
  {
    if (instance == null)
    {
      instance = new NewCarDAO();
    }
    return instance;
  }//getInstance

  ////////////////////////////////////////////
  ///   CREATE methods

  public NewCar create(String id) throws DataException
  {
    NewCar newCar = new NewCar(id);
    newCar.setObjectAlreadyInDB(false);
    Cache.getInstance().put(newCar.getId(), newCar);
    return newCar;
  }//create


  ////////////////////////////////////////////
  ///   READ methods

  public NewCar read(String id) throws DataException
  {
    if (Cache.getInstance().containsKey(id))
    {
      return (NewCar)Cache.getInstance().get(id);
    }
    Connection conn = ConnectionPool.getInstance().get();
    try
    {
      return read(id, conn);
    }
    catch (Exception e)
    {
      throw new DataException("An error occurred while reading the business object information.", e);
    }
    finally
    {
      ConnectionPool.getInstance().release(conn);
    }
  }

  synchronized NewCar read(String id, Connection conn) throws Exception
  {
    if (Cache.getInstance().containsKey(id))
    {
      return (NewCar)Cache.getInstance().get(id);
    }
    PreparedStatement stmt = conn.prepareStatement("select * FROM new_car WHERE new_car.id = ?");
    try
    {
      stmt.setString(1, id);
      ResultSet rs = stmt.executeQuery();
      if (rs.next())
      {
        return readRecord(rs, conn);
      }//if
      throw new DataException("New car with id '" + id + "' not found.");
    }
    finally
    {
      stmt.close();
    }
  }//read


 
  synchronized NewCar readRecord(ResultSet rs, Connection conn) throws Exception
  {
    if (Cache.getInstance().containsKey(rs.getString("id")))
    {
      return (NewCar)Cache.getInstance().get(rs.getString("id"));
    }
    NewCar newCar = new NewCar(rs.getString("id"));
    newCar.setObjectAlreadyInDB(true);
    newCar.setMSRP(Double.parseDouble(rs.getString("MSRP")));
    PhysicalCarDAO.getInstance().read(newCar.getId(), conn);
    //SaleLineDAO.getInstance().getByNewCarID(rs.getString("id"));

    Cache.getInstance().put(newCar.getId(), newCar);
    newCar.setDirty(false);
    return newCar;
  }//readRecord

  /////////////////////////////////////////////
  ///   UPDATE methods

  /** Saves an existing newCar in the database */
  public void save(NewCar newCar) throws DataException
  {
  Connection conn = ConnectionPool.getInstance().get();
    try
    {
      save(newCar, conn);
      conn.commit();
    }
    catch (Exception e)
    {
      try
      {
        conn.rollback();
      }
      catch (SQLException e2)
      {
        throw new DataException("Could not roll back the database transaction!", e2);
      }
      throw new DataException("An error occurred while saving the business object information.", e);
    }
    finally
    {
      ConnectionPool.getInstance().release(conn);
    }
  }

  /** Internal method to update a newCar in the database */
  void save(NewCar newCar, Connection conn) throws Exception
  {
    Cache.getInstance().put(newCar.getId(), newCar);
    if (newCar.isObjectAlreadyInDB() && newCar.isDirty())
    {
      update(newCar, conn);
    }
    else if (newCar.isDirty())
    {
      insert(newCar, conn);
    }
  }

  /** Saves an existing newCar to the database */
  private void update(NewCar newCar, Connection conn) throws Exception
  {
  PreparedStatement stmt = conn.prepareStatement("UPDATE new_car SET MSRP=? WHERE id=?");
    try
    {
      PhysicalCarDAO.getInstance().save(newCar, conn);
      stmt.setString(1, newCar.getMSRP() + "");
      stmt.setString(2, newCar.getId());
      stmt.execute();
      newCar.setDirty(false);
    }
    finally
    {
      stmt.close();
    }
 }

  /** Inserts a new newCar into the database */
  private void insert(NewCar newCar, Connection conn) throws Exception
  {
  PreparedStatement stmt = conn.prepareStatement("INSERT INTO new_car VALUES (?, ?)");
    try
    {
      PhysicalCarDAO.getInstance().save(newCar, conn);
      stmt.setString(1, newCar.getId());
      stmt.setString(2, newCar.getMSRP() + "");
      stmt.execute();
      newCar.setObjectAlreadyInDB(true);
      newCar.setDirty(false);
    }
    finally
    {
      stmt.close();
    }
  }





  /////////////////////////////////////////////////
  ///   DELETE methods

  /** We do not support deleting of business objects in this application */
  public void delete(NewCar newCar) throws DataException
  {
    throw new RuntimeException("Nice try. The delete function is not supported in this application.");
  }



  ////////////////////////////////////////////////
  ///   SEARCH methods

  /** Retrieves all conceptualcars from the database */
  public List<NewCar> getAll() throws DataException
  {
    Connection conn = ConnectionPool.getInstance().get();
    try
    {
      PreparedStatement stmt = conn.prepareStatement("SELECT *  FROM physical_car, new_car WHERE new_car.id = physical_car.id ORDER BY physical_car.id");
      return search(stmt, conn);
    }
    catch (Exception e)
    {
      try
      {
        conn.rollback();
      }
      catch (SQLException e2)
      {
        throw new DataException("Could not roll back the database transaction!", e2);
      }
      throw new DataException("An error occurred while reading the business object information.", e);
    }
    finally
    {
      ConnectionPool.getInstance().release(conn);
    }  //establish a connection, use a prepared statement, plug it into the search method return a list of empoyees
  }

  /** Internal method to search by certain fields */
  private List<NewCar> search(PreparedStatement stmt, Connection conn) throws Exception
  {
    List<NewCar> pcList = new LinkedList<NewCar>();
    try
    {
      ResultSet rs = stmt.executeQuery();
      while (rs.next())
      {
        pcList.add(readRecord(rs, conn));
      }
    }
    finally
    {
      stmt.close();
    }
    return pcList;
  }

    public NewCar readByVIN(String VIN) throws DataException
  {
    Connection conn = ConnectionPool.getInstance().get();
    try
    {
      return readByVIN(VIN, conn);
    }
    catch (Exception e)
    {
      throw new DataException("An error occurred while reading the business object information.", e);
    }
    finally
    {
      ConnectionPool.getInstance().release(conn);
    }
  }

  /**
   * Internal method to read an existing Physical Car from the database
   * 1) Check cache for object
   * 2) Create a prepared statement to execute sql query
   * 3) Create a resultset from the preparedstatement
   * 4) Return the Physical Car
   */
  synchronized NewCar readByVIN(String VIN, Connection conn) throws Exception
  {
    PreparedStatement stmt = conn.prepareStatement("select * FROM physical_car, new_car WHERE VIN = ? AND new_car.id = physical_car.id");
    try
    {
      stmt.setString(1, VIN);
      ResultSet rs = stmt.executeQuery();
      if (rs.next())
      {
        return readRecord(rs, conn);
      }//if
      throw new DataException("Salesperson with VIN '" + VIN + "' not found.");
    }
    finally
    {
      stmt.close();
    }
  }//read

}
